/****************************************************************************
  From Qt Examples
****************************************************************************/

#include "simuhost.h"
#include "server.h"
#include "serverthread.h"
#include <QDebug>
#include <QtNetwork>
#include <stdlib.h>

Server::Server(QObject *parent)
    : QTcpServer(parent)
{
    clockSpeed = 1.0;
    ipc = 1.0;
    MTTF = 31556926;
    MTTR = 86400;
    message = "";
    SDescriptor = 0;
    //connection = new QTcpSocket();
    proxyBlockSize = 0;
    serverSim = NULL;
}

/*
void Server::ReadCMsg()
{
    //qDebug() << "MSG Recived";

    QDataStream in(connection);
    in.setVersion(QDataStream::Qt_4_0);

    if (blockSize == 0) {
        if (connection->bytesAvailable() < (int)sizeof(quint16))
            return;
        in >> blockSize;
    }

    //qDebug() << "BlockSize: " << blockSize;

    if (connection->bytesAvailable() < blockSize)
        return;

    QString nextMSG;
    in >> nextMSG;

    currentMSG = nextMSG;
    //qDebug() << currentMSG;
    //statusLabel->setText(currentMessage);
    //clientTextEdit->append(currentMessage);
    //getMessageButton->setEnabled(true);
    blockSize = 0;
}
*/

QString Server::getCMSG()
{
    return currentMSG;
}

void Server::setMsg(QString msg)
{
    message = msg;
}

// create new client thread
void Server::incomingConnection(int socketDescriptor)
{
    //qDebug() << socketDescriptor << " Connecting...";

    SDescriptor = socketDescriptor;

    //associate the socket descriptor with name/pass of a client to know which client to send what...
    socketDescs.append(SDescriptor);
    // connection->setSocketDescriptor(SDescriptor);

    //For reading the data from the client
    // connect(connection, SIGNAL(readyRead()), this, SLOT(ReadCMsg()));
    ServerThread *thread = new ServerThread(SDescriptor, message, this);
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    connect(thread,SIGNAL(clientMessage(QString)),this,SLOT(getMSG(QString)));
    connect(this,SIGNAL(sendInfo(QString)),thread,SLOT(sendServerInformation(QString)));
    thread->start();
}

void Server::getMSG(QString msg)
{
    message = msg;
    emit transferMessage(message);
    QStringList list = message.split(';');
    foreach(QString clientLoad, list) {
        serverSim->allocateLoad(clientLoad.section(':', 1, 1).toULongLong());
    }
}

// manage proxy connection
void Server::ProxyConnection(QString IP, int port)
{
    proxySocket = new QTcpSocket(this);
    proxyBlockSize = 0;
    connect(proxySocket, SIGNAL(connected()), this, SLOT(startServerSim()));
    connect(proxySocket, SIGNAL(readyRead()), this, SLOT(recieveProxyData()));
    connect(proxySocket, SIGNAL(disconnected()), this, SLOT(stopServerSim()));

    proxySocket->abort();
    //qDebug() << "Connecting to " << IP << " on port " << port;
    proxySocket->connectToHost(IP,port);
    //qDebug() << "Server connected to the proxy";
}

void Server::sendServerData(QString serverData)
{
    //OLD CODE...
    // //qDebug() << "Emitting server Data to the ServerThread: " << serverData;
    // emit sendInfo(serverData);

    //qDebug() << "Socket: "  << proxySocket->socketDescriptor();


    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_0);
    out << (quint16)0; //allocate space for the size variable
    out << serverData.prepend("server::"); // write the message
    out.device()->seek(0); //go back to front of black array
    out << (quint16)(block.size() - sizeof(quint16)); //put in the size in previously reserved space

    //qDebug() << "Server writing to Proxy: " << serverData << endl;
    //qDebug() << "Block: " << block;
    int test;
    test = proxySocket->write(block);//send to server
    //if(test == -1)
        //qDebug() << "Cant write";
    //else
        //qDebug() << "Server sent msg..." << endl;
}

// rec
void Server::recieveProxyData()
{
    ////qDebug() << "Server " << proxySocket->socketDescriptor() << " reading.";
    ////qDebug() << "Original blockSize: " << proxyBlockSize;
    QDataStream in(proxySocket);
    in.setVersion(QDataStream::Qt_4_0);

    if (proxyBlockSize == 0) {
        if (proxySocket->bytesAvailable() < (int)sizeof(quint16))
            return;
        in >> proxyBlockSize;
    }

    ////qDebug() << "BlockSize: " << proxyBlockSize;

    if (proxySocket->bytesAvailable() < proxyBlockSize)
        return;

    //Write the proxy data
    in >> proxyData;

    ////qDebug() << "Recieved the full msg from the proxy: \n" + proxyData;

    // create new server simulation if it doesn't exist
    if (serverSim == NULL) {
        // read values from proxy
        QStringList serverVals = proxyData.split("::", QString::KeepEmptyParts);

        if (!serverVals.isEmpty()) {
            //qDebug() << "Creating server simulation";
            serverSim = new SimuHost(serverVals.first().toInt(),
                                  serverVals.takeFirst().toInt(),
                                  serverVals.takeFirst().toInt(),
                                  clockSpeed, ipc, MTTF, MTTR);
            serverSim->turnOn(true);
        }
    }
    else {
        qDebug() << "Syncing";
        if (serverSim->sync(proxyData.toULongLong())) {
            qDebug() << "Synced";
        }
        else {
            qDebug() << "Could not sync";
        }

        sendSimUpdate();
    }

    proxyBlockSize = 0;
}

void Server::startServerSim()
{
    QString serverData = this->serverAddress().toString()
            + "::" + QString::number(this->serverPort(),10);
    sendServerData(serverData);
}

void Server::stopServerSim()
{
    serverSim->turnOn(false);
    delete serverSim;
}

void Server::setHardwareSpec(double clocks, double iPc, unsigned int mttf, unsigned int mttr)
{
    clockSpeed = clocks;
    ipc = iPc;
    MTTF = mttf;
    MTTR = mttr;
}

void Server::sendSimUpdate()
{
    qDebug() << "sending update";
    if (serverSim->hardwareFailed()) {
        sendServerData("hardware failure");
    }
    else {
        qDebug() << "Current Load: " << QString::number(serverSim->currentLoad());
        sendServerData(QString::number(serverSim->loadPercentage()).prepend("load::"));
    }
}
